package com.example.jobback.util;

import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName: CACHEUtil
 * @Description: redis缓存工具类
 * @Author: lhb
 * @Date: 2025/5/10
 */


@Component
public class CACHEUtil {

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    
    @Resource
    private RedisConnectionFactory redisConnectionFactory;


    public void setHash(String key, Map<String, Object> userMap, Long time, TimeUnit unit) {
        stringRedisTemplate.opsForHash().putAll(key,userMap);
        stringRedisTemplate.expire(key,time,unit);
    }

    public Map<Object, Object> getHash(String key) {
        return stringRedisTemplate.opsForHash().entries(key);
    }

    public void expire(String key, Long time, TimeUnit unit) {
        stringRedisTemplate.expire(key,time,unit);
    }


    /**
     * @Description: 只有key存在被删除才返回true
     * @Author lhb
     * @CreateTime 2025/5/16 16:33
     */
    public boolean deleteIfExists(String key){
        return Boolean.TRUE.equals(stringRedisTemplate.delete(key));
    }


    /**
     * @Description: 只有操作不成功才返回false
     * @Author lhb
     * @CreateTime 2025/5/16 16:34
     */
    public boolean deleteKeySafely(String key) {
        Boolean exists = stringRedisTemplate.hasKey(key);
        if(!exists) return true;
        return Boolean.TRUE.equals(stringRedisTemplate.delete(key));
    }

    public void updateHash(String key, String data, String field) {
    }

    public void set(String key, String value, Long time, TimeUnit unit) {
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value),time,unit);
    }


    public void setNoTtl(String key, String value){
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value));
    }

    public String get(String key) {
        String value=stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.isNotBlank(value)) return value;
        return null;
    }

    /**
     * @Description: 不建议使用
     * @param keys
     * @return List<String>
     * @Author lhb
     * @CreateTime 2025/5/31 17:18
     */
    public List<String> multiGet(List<String> keys){
        return stringRedisTemplate.opsForValue().multiGet(keys);
    }

    public void setZSet(String key, String member, double score) {
        stringRedisTemplate.opsForZSet().add(key, member, score);
    }

    /**
     * @Description: 向 Redis Set 中添加一个或多个字符串值
     * @param key
     * @param values
     * @return void
     * @Author lhb
     * @CreateTime 2025/7/15 10:41
     */
    public void setSet(String key, String... values) {
        stringRedisTemplate.opsForSet().add(key, values);
    }

    /**
     * @Description: 向 Redis Set 中添加一个集合中的字符串值
     * @param key
     * @param values
     * @return void
     * @Author lhb
     * @CreateTime 2025/7/15 10:42
     */
    public void setSet(String key, Collection<String> values) {
        stringRedisTemplate.opsForSet().add(key, values.toArray(new String[0]));
    }

    /**
     * @Description: 判断用户是否在set集合内
     * @param key
     * @param member 
     * @return boolean
     * @Author lhb
     * @CreateTime 2025/7/15 11:33
     */
    public boolean isMemberInSet(String key, String member) {
        return stringRedisTemplate.opsForSet().isMember(key, member);
    }

    /**
     * @Description: 返回set所有元素
     * @param key
     * @return Set<String>
     * @Author lhb
     * @CreateTime 2025/7/15 11:34
     */
    public Set<String> getAllMembersFromSet(String key) {
        return stringRedisTemplate.opsForSet().members(key);
    }



    public void removeMember(String key, String member) {
        stringRedisTemplate.opsForZSet().remove(key, member);
    }

    
    /**
     * @Description: 移除多个成员
     * @Author lhb
     * @CreateTime 2025/5/17 20:06
     */
    public void removeMembers(String key, String... members) {
        stringRedisTemplate.opsForZSet().remove(key, members);
    }

    public Set<String> getMembersByRank(String key, long start, long end) {
        return stringRedisTemplate.opsForZSet().range(key, start, end);
    }


    public boolean isMemberInZSet(String key, String member) {
        // 使用 opsForZSet() 获取有序集合操作对象
        Double score = stringRedisTemplate.opsForZSet().score(key, member);
        // 如果 score 不为 null，说明成员存在
        return score != null;
    }



    /**
     * @Description: 逆序
     * @Author lhb
     * @CreateTime 2025/5/17 20:08
     */
    public Set<String> getMembersByReverseRank(String key, long start, long end) {
        return stringRedisTemplate.opsForZSet().reverseRange(key, start, end);
    }

    public void putHash(String key, String hashKey, String value) {
        stringRedisTemplate.opsForHash().put(key,hashKey,value);
    }

    /**
     * @Description: 移除ZSet中分数在[minScore, maxScore]范围内的元素（用于清理窗口外的请求记录）
     * @param key ZSet的键
     * @param minScore 分数下限（包含）
     * @param maxScore 分数上限（包含）
     * @return Long 被移除的元素数量
     * @Author lhb
     * @CreateTime 2025/9/2 21:47
     */
    public Long removeZSetRangeByScore(String key, double minScore, double maxScore) {
        return stringRedisTemplate.opsForZSet().removeRangeByScore(key, minScore, maxScore);
    }

    /**
     * @Description: 获取ZSet的元素总数
     * @param key ZSet的键
     * @return Long 元素总数（若key不存在，返回0）
     * @Author lhb
     * @CreateTime 2025/9/2 21:48
     */
    public Long getZSetSize(String key) {
        return stringRedisTemplate.opsForZSet().zCard(key);
    }


    /**
     * @Description: 添加记录到stream
     * @param streamKey stream的键名
     * @param record 记录
     * @return void
     * @Author lhb
     * @CreateTime 2025/9/20 21:26
     */
    public void addStreamRecord(String streamKey, Map<String, String> record) {
        stringRedisTemplate.opsForStream().add(streamKey, record);
    }

    /**
     * @Description: 创建消费者组
     * @param streamKey key
     * @param groupName  消费者组名称
     * @return void
     * @Author lhb
     * @CreateTime 2025/9/20 21:33
     */
    public void createStreamGroup(String streamKey, String groupName) {
        try {
            //检查Stream是否存在，不存在则创建（添加初始化消息，避免空Stream）
            Boolean streamExists = stringRedisTemplate.hasKey(streamKey);
            if (BooleanUtils.isFalse(streamExists)) {
                stringRedisTemplate.opsForStream().add(streamKey,
                        Collections.singletonMap("init", "stream_init"));
            }
            //检查消费者组是否已存在（主动查询，避免抛出异常）
            if(isConsumerGroupExists(streamKey, groupName)) {
                return;
            }
            //不存在则创建组（从Stream开头开始消费）
            stringRedisTemplate.opsForStream().createGroup(
                    streamKey,
                    ReadOffset.from("0"),
                    groupName
            );

        } catch (Exception e) {
            // 其他异常（如Redis连接失败）需要抛出
            throw new RuntimeException("创建Stream消费者组异常", e);
        }
    }

    /**
     * @param streamKey    Stream键名
     * @param groupName    消费者组名称
     * @param consumerName 消费者名称
     * @param count        读取数量
     * @return 消息记录列表
     * @Description: 从Stream中读取消息
     * @Author lhb
     * @CreateTime 2025/9/20
     */
    public List<MapRecord<String, Object, Object>> readStreamMessages(
            String streamKey, String groupName, String consumerName, int count) {
        Consumer consumer = Consumer.from(groupName, consumerName);
        StreamReadOptions options = StreamReadOptions.empty().count(count).block(Duration.ofSeconds(1));
        return stringRedisTemplate.opsForStream().read(consumer, options,
                StreamOffset.create(streamKey, ReadOffset.lastConsumed()));
    }

    /**
     * @Description: 确认消息已处理
     * @param streamKey Stream键名
     * @param groupName 消费者组名称
     * @param messageIds 消息ID数组
     * @Author lhb
     * @CreateTime 2025/9/20
     */
    public void acknowledgeStreamMessage(String streamKey, String groupName, String... messageIds) {
        stringRedisTemplate.opsForStream().acknowledge(groupName, streamKey, messageIds);
    }

    /**
     * @Description: 判断消费者组是否存在
     * @param streamKey
     * @param groupName 
     * @return boolean
     * @Author lhb
     * @CreateTime 2025/9/20 22:54
     */
    public boolean isConsumerGroupExists(String streamKey, String groupName) {
        try (RedisConnection connection = redisConnectionFactory.getConnection()) {
            StreamInfo.XInfoGroups groups = connection.streamCommands().xInfoGroups(streamKey.getBytes());
            return groups.stream().anyMatch(group -> group.groupName().equals(groupName));
        }
    }




}
